﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using Google.Protobuf;
using System.Threading;
using Google.Protobuf.Examples.AddressBook;

namespace UdpPB_Demo
{
    class Program
    {
            private static List<string> messages = new List<string>();

            private static void SendThread(List<TcpClient> connections)
            {
                while (true)
                {
                    string str = null;
                    lock (messages)
                    {
                        if (messages.Count > 0)
                        {
                            str = messages[0];
                            messages.RemoveAt(0);
                        }
                    }
                    if (str != null)
                    {
                        var buff = Encoding.ASCII.GetBytes(str);
                        lock (connections)
                        {
                            foreach (var c in connections)
                            {
                                try
                                {
                                    //c.Send(buff);
                                    c.GetStream().Write(buff, 0, buff.Length);
                                }
                                catch (Exception exp)
                                {
                                    Console.WriteLine(exp.ToString());
                                }
                            }
                        }
                    }
                    Thread.Sleep(100);
                }
            }

            private static Dictionary<int, System.Type> idToProtocols = new Dictionary<int, Type>()
        {
            {1, typeof(MovePosC2S)},
            {2, typeof(UseSkillC2S)},
            {3, typeof(Data)},
        };
            public class DataStream
            {
                public byte[] buffer = new byte[1024];
                public int readPos = 0;
                public int writePos = 0;

                public void Clear()
                {
                    readPos = writePos = 0;
                }
                public UInt16 ReadUInt16()
                {
                    return BitConverter.ToUInt16(buffer, 0);
                }
                public byte ReadByte()
                {
                    return buffer[0];
                }

                public IMessage ReadData(int typeId)
                {
                    var type = idToProtocols[typeId];
                    var msg = Activator.CreateInstance(type) as IMessage;

                    var ci = new Google.Protobuf.CodedInputStream(buffer, 0, writePos);
                    msg.MergeFrom(ci);
                    return msg;
                }
            }


            public class MessageProcess
            {
                public ReadState state = ReadState.HEAD;
                public DataStream dataStream = new DataStream();
                private int expectLen = 2;

                private int bodyLength = 0;
                private int typeId = 0;

                public IMessage msgData;
                public void Process(byte[] data, int n)
                {
                    var readYet = 0;

                    while (n > 0)
                    {
                        if (state == ReadState.HEAD)
                        {
                            if (n >= expectLen)
                            {
                                Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
                                readYet += expectLen;
                                dataStream.writePos += expectLen;
                                n -= expectLen;

                                bodyLength = dataStream.ReadUInt16();
                                dataStream.Clear();
                                expectLen = 2;

                                state = ReadState.TYPE;
                                Console.WriteLine("BodyLength: " + bodyLength.ToString());
                            }
                            else
                            {
                                Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
                                readYet += n;
                                dataStream.writePos += n;
                                n -= n;
                            }
                        }
                        else if (state == ReadState.TYPE)
                        {
                            if (n >= expectLen)
                            {
                                Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
                                readYet += expectLen;
                                dataStream.writePos += expectLen;
                                n -= expectLen;

                                typeId = dataStream.ReadUInt16();
                                dataStream.Clear();

                                expectLen = 1;
                                state = ReadState.FRAME_ID;
                                Console.WriteLine("TypeId: " + typeId);
                            }
                            else
                            {
                                Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
                                readYet += n;
                                dataStream.writePos += n;
                                n -= n;
                            }
                        }
                        else if (state == ReadState.FRAME_ID)
                        {
                            if (n >= expectLen)
                            {
                                Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
                                readYet += expectLen;
                                dataStream.writePos += expectLen;
                                n -= expectLen;

                                var data1 = dataStream.ReadByte();
                                Console.WriteLine("FrameId:" + data1.ToString());
                                dataStream.Clear();
                                expectLen = bodyLength;

                                state = ReadState.BODY;
                            }
                            else
                            {
                                Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
                                readYet += n;
                                dataStream.writePos += n;
                                n -= n;
                            }
                        }
                        else
                        {
                            if (n >= expectLen)
                            {
                                Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, expectLen);
                                readYet += expectLen;
                                dataStream.writePos += expectLen;
                                n -= expectLen;

                                var data1 = dataStream.ReadData(typeId);
                                msgData = data1;
                                Console.WriteLine(data1.ToString());

                                dataStream.Clear();
                                expectLen = 2;

                                state = ReadState.HEAD;
                            }
                            else
                            {
                                Array.Copy(data, readYet, dataStream.buffer, dataStream.writePos, n);
                                readYet += n;
                                dataStream.writePos += n;
                                n -= n;
                            }
                        }
                    }
                }
            }


            public enum ReadState
            {
                HEAD,
                TYPE,
                FRAME_ID,
                BODY,
            } ;

            private static async void ProcessSocket(TcpClient client)
            {
                var stream = client.GetStream();
                var buffer = new byte[1024];
                var messageProcess = new MessageProcess();

                while (true)
                {
                    Console.WriteLine("Before receive");

                    var n = 0;
                    try
                    {
                        n = await stream.ReadAsync(buffer, 0, 1024);
                    }
                    catch (Exception exp)
                    {
                        Console.WriteLine(exp.ToString());
                    }
                    Console.WriteLine("Server Receive: " + n);

                    if (n == 0)
                    {
                        try
                        {
                            client.Close();
                        }
                        catch (Exception exp)
                        {
                            Console.WriteLine(exp.ToString());
                        }

                        lock (connections)
                        {
                            connections.Remove(client);
                        }
                        break;
                    }
                    else
                    {
                        messageProcess.Process(buffer, n);
                    }

                }
            }

            private static void AsyncClient(TcpClient client)
            {
                ProcessSocket(client);
            }


            private static List<TcpClient> connections = new List<TcpClient>();
            public static void Main(string[] args)
            {

                var udpListener = new TestUDPServer();


                var sendThread = new Thread(() =>
                {
                    SendThread(connections);
                });
                sendThread.Start();

                while (true)
                {

                    udpListener.Receive();
                }
            }
        
    }
}
